package com.zidongxiangxi.clock.server.config;

import com.alibaba.fastjson.JSON;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;

import javax.annotation.PreDestroy;
import java.util.Objects;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 异步线程配置类
 *
 * @author chenxudong
 * @date 2020/06/29
 */
@Slf4j
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
    /**
     * 核心线程数
     */
    private static final int CORE_SIZE = 30;

    /**
     * 最大线程数
     */
    private static final int MAX_SIZE = CORE_SIZE * 2;

    /**
     * 存活时间
     */
    private static final int KEEP_ALIVE_TIME = 10;

    /**
     * 队列
     */
    private static final BlockingQueue<Runnable> WORK_QUEUE = new LinkedBlockingQueue<>(1024);

    /**
     * 持有自己new的线程池，方便关闭
     */
    private ThreadPoolExecutor threadPoolExecutor = null;

    @Bean("asyncExecutor")
    public ThreadPoolExecutor asyncExecutor() {
        threadPoolExecutor = new ThreadPoolExecutor(CORE_SIZE, MAX_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS,
            WORK_QUEUE, new ThreadFactoryBuilder().setNameFormat("CLOCK_THREAD_%d").build(),
            new ThreadPoolExecutor.CallerRunsPolicy());
        return threadPoolExecutor;
    }

    @PreDestroy
    public void preDestroy() {
        if (Objects.nonNull(threadPoolExecutor) && !threadPoolExecutor.isTerminated()) {
            threadPoolExecutor.shutdown();
            boolean isTermination = false;
            while (!isTermination) {
                try {
                    isTermination = threadPoolExecutor.awaitTermination(1, TimeUnit.SECONDS);
                } catch (InterruptedException e) {
                    log.warn("break when wait for job finish", e);
                    Thread.currentThread().interrupt();
                }
            }
        }
        log.info("default executor queue is clear!");
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return (ex, method, params) ->
            log.error("async deal error：method = {},params = {}", method.getName(), JSON.toJSONString(params), ex);
    }

}
